package arc

import (
	"encoding/json"
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/validator/v10"
	"reflect"
)


type ErrorResult struct {
	err    error
	status int
	data   interface{}
}

func NewErrorResult(err error, status int, data interface{}) *ErrorResult {
	return &ErrorResult{err: err, data: data, status: status}
}

func NewErrResultWithStr(err string) *ErrorResult {
	return &ErrorResult{
		err: errors.New(err),
	}
}

func (e *ErrorResult) SetStatus(status int) Error {
	e.status = status
	return Error(e)
}
func (e *ErrorResult) GetStatus() int {
	return e.status
}

func (e *ErrorResult) SetError(err error) Error {
	e.err = err
	return Error(e)
}

func (e *ErrorResult) SetErrWithStr(err string) Error {
	e.err = errors.New(err)
	return Error(e)
}

func (e *ErrorResult) SetData(data interface{}) Error {
	e.data = data
	return Error(e)
}

func (e *ErrorResult) GetData() interface{} {
	return e.data
}

func (e *ErrorResult) Error() string {
	return e.err.Error()
}

func (e *ErrorResult) GetError() error {
	return e.err
}

func (e *ErrorResult) Unwrap() interface{} {
	if e.err != nil {
		panic(e)
	}
	return e.data
}

func (e *ErrorResult) UnwrapOr(v interface{}) interface{} {
	if e.err != nil {
		return v
	}
	return e.data
}

func (e *ErrorResult) UnwrapOrElse(f func() interface{}) interface{} {
	if e.err != nil {
		return f()
	}
	return e.data
}

// Result  0 data 1 err
func Result(vs ...interface{}) *ErrorResult {
	if len(vs) == 1 {
		switch vs[0].(type) {
		case Error:
			e := vs[0].(Error)
			return NewErrorResult(e.GetError(), e.GetStatus(), nil)
		case error:
			e := vs[0].(error)
			return NewErrorResult(e, StatusInternalServerError, nil)
		default:
			return &ErrorResult{}
		}
	}
	if len(vs) == 2 {
		switch vs[1].(type) {
		case Error:
			e := vs[0].(Error)
			return NewErrorResult(e.GetError(), e.GetStatus(), nil)
		case error:
			e := vs[1].(error)
			return NewErrorResult(e, StatusInternalServerError, vs[0])
		default:
			return NewErrorResult(nil, 200, vs[0])
		}
	}
	return NewErrorResult(errors.New("error result format"), StatusInternalServerError, nil)
}

func (e *ErrorResult) JSON() interface{} {
	jsonData := gin.H{}
	if e.data != nil {
		value := reflect.ValueOf(e.data)
		switch value.Kind() {
		case reflect.Struct:
			return e.data
		case reflect.Map:
			if e.err != nil {
				var errList []interface{}
				for _, k := range value.MapKeys() {
					errList = append(errList, value.MapIndex(k).Interface())
				}
				jsonData["errors"] = errList
			} else {
				for _, k := range value.MapKeys() {
					jsonData[k.String()] = value.MapIndex(k).Interface()
				}
			}

		default:
			jsonData["data"] = e.data
		}
	}

	if _, ok := jsonData["msg"]; !ok {
		jsonData["msg"] = e.err.Error()
	}
	return jsonData
}

func (e *ErrorResult) MarshalJSON() ([]byte, error) {
	return json.Marshal(e.JSON())
}

// BindFunc request bind  value
type BindFunc func(v interface{}) error

func Exec(f BindFunc, v interface{}) *ErrorResult {
	err := f(v)
	var errs validator.ValidationErrors
	if err != nil {
		var ok bool
		errs, ok = err.(validator.ValidationErrors)
		if !ok {
			return NewErrorResult(err, StatusInternalServerError, nil)
		}
		return NewErrorResult(errors.New("参数错误！"), StatusBadRequest, errs.Translate(trans))
	}
	return NewErrorResult(err, StatusOK, v)
}

// BindWithFunc request bind with value  func: ShouldBindWith
type BindWithFunc func(v interface{}, b binding.Binding) error

func ExecWith(f BindWithFunc, v interface{}, binding binding.Binding) *ErrorResult {
	err := f(v, binding)
	var errs validator.ValidationErrors
	if err != nil {
		var ok bool
		errs, ok = err.(validator.ValidationErrors)
		if !ok {
			return NewErrorResult(err, StatusInternalServerError, nil)
		}
		return NewErrorResult(errors.New("参数错误！"), StatusBadRequest, errs.Translate(trans))
	}

	return NewErrorResult(err, StatusOK, v)

}
